home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / DDJMAG / DDJ8904.ZIP / SWAP.ASC < prev    next >
Text File  |  1989-03-27  |  44KB  |  1,093 lines

  1. _SWAP_
  2. by Nico Mak
  3.  
  4. [LISTING ONE]
  5.  
  6.  
  7. ; SWAP - (c) Copyright 1988 Nico Mak and Mansfield Software Group
  8. ; All rights reserved
  9. ;
  10. ; To rebuild SWAP.COM use the following instructions:
  11. ;   masm swap;
  12. ;   link swap;
  13. ;   exe2bin swap swap.com
  14. ;
  15. cr              equ     13
  16. lf              equ     10
  17.  
  18. error   macro   message                 ;; macro to display an error message
  19.         local   around, msg, msglen     ;; and to jump to error_exit routine
  20.         jmp     around
  21. msg     db      &message,cr,lf          ;; define error message
  22. msglen  equ     $-msg
  23. around:
  24.         mov     dx,offset msg           ;; get address of error message
  25.         mov     cx,msglen               ;; get length
  26.         jmp     error_exit              ;; jump to error exit routine
  27.         endm
  28.  
  29. ; -------------------------------------------------------------------
  30. ; the following is copied over the swappee
  31. ; -------------------------------------------------------------------
  32. code    segment 'code'
  33.         assume  cs:code,ds:code
  34.         org     100h                    ; org past psp
  35. swap    proc    near
  36.         jmp     begin
  37.                 db      20 dup('STACK')
  38. stack           equ     $
  39.  
  40. flag            db      0       ; option flag
  41. flag_copy       equ     80h     ; copy stdout to 'con'
  42. flag_force      equ     40h     ; swap even if vector points to swappee
  43. flag_quiet      equ     20h     ; don't print hello message
  44. flag_disk       equ     10h     ; swap to disk
  45.  
  46. emsg_ems        db      "SWAP EMS Error "
  47. ems_rc          db      'xx function '
  48. ems_func        db      'xx',cr,lf
  49. emsg_ems_len    equ     $-emsg_ems
  50.  
  51. my_psp          dw      ?       ; segment of SWAP's original psp
  52. swappee_psp     dw      ?       ; segment of swappee's psp
  53.  
  54. ; variables used when swapping to expanded memory
  55. ems_handle      dw      ?       ; emm handle
  56. swap_pages      dw      ?       ; number of pages for ems_handle
  57. ems_frame       dw      ?       ; ems page frame
  58. last_ems_func   db      ?       ; last emm function issued by swap
  59.  
  60. ; variables used when swapping to disk
  61. swap_fid        db      "c:\swap.dat",0 ; asciiz string to open swap file
  62. swap_handle     dw      ?       ; handle while swap file is open
  63.  
  64. ; fields for int 21 function 4b (exec)
  65. commandcom_addr dd      ?       ; address of program to exec (command.com)
  66. exec_sp         dw      ?       ; save area for reg clobbered by exec function
  67. command_line    db      ?,"/c"          ; command line for command.com
  68. command_text    db      130 dup (0)     ; command line continued
  69. blank_fcb       db      36 dup (0)      ; dummy fcb for exec function
  70. exec_parm_block equ     $               ; exec parameter block
  71. exec_env        dw      ?               ; segment addr of environment
  72. cmdline_addr    dw      offset command_line     ; address of command line
  73. cmdline_seg     dw      ?
  74.                 dw      offset blank_fcb        ; address of fcb
  75. fcb1_seg        dw      ?
  76.                 dw      offset blank_fcb        ; address of fcb
  77. fcb2_seg        dw      ?
  78.  
  79. ; fields used by int 21 handler
  80. save_pid        dw      ?       ; pid at time int 21 handler received control
  81. int21_vector    dd      ?       ; original int 21 vector owner
  82. con             db      "con",0 ; asciiz string to open console
  83. handle          dw      ?       ; handle while "con" is open
  84. char_buf        db      ?       ; buffer for int 21 function 2 and 6 handlers
  85. save_ax         dw      ?       ; register save areas for int 21 handler
  86. save_bx         dw      ?
  87. save_cx         dw      ?
  88. save_dx         dw      ?
  89. save_ds         dw      ?
  90.  
  91. ; -------------------------------------------------------------------
  92. ; run_command - the following code is copied over the swappee
  93. ; -------------------------------------------------------------------
  94. run_command:
  95.         call    copy_start              ; start copying stdout to the console
  96.         call    exec_user_cmd           ; execute the user's command
  97.         call    copy_stop               ; stop copying stdout to the console
  98.         call    swap_in                 ; swap in all but first 16k
  99.         retf
  100.  
  101. ; -------------------------------------------------------------------
  102. ; subroutines for run_command follow
  103. ; -------------------------------------------------------------------
  104.  
  105. ; -----
  106. ; copy_start - if -c option specified, open handle for console and hook int 21
  107. ; -----
  108. copy_start:
  109.         test    flag,flag_copy          ; will we copy stdout to display?
  110.         jz      copy_start_ret          ; no
  111. ; ----- open a handle that points to "con"
  112.         mov     dx,offset con           ; address of asciiz file name
  113.         mov     ax,3d01h                ; code to open handle for writing
  114.         int     21h                     ; open the file
  115.         mov     handle,ax               ; remember handle
  116.         jnc     open_worked             ; did open succeed?
  117.         and     flag,255-flag_copy      ; no, then we won't copy stdout ...
  118.         jmp     short copy_start_ret    ; ... and won't hook int 21
  119. open_worked:
  120. ; ----- hook int 21 vector
  121.         mov     ax,3521h                ; code to get interrupt 21 vector
  122.         int     21h                     ; ask dos for address in vector
  123.         mov     word ptr int21_vector,bx; save offset
  124.         mov     word ptr int21_vector[2],es ; save segment
  125.         mov     dx,offset int21_handler ; address of our int 21 handler
  126.         mov     ax,2521h                ; code to set interrupt 21 address
  127.         int     21h                     ; tell dos to set int 21 vector
  128. ; ----- ensure that standard error is redirected and copied
  129.         mov     al,cs:[19h]             ; get stdout file handle array entry
  130.         mov     cs:[1ah],al             ; use stdout entry for stderr entry
  131. copy_start_ret:
  132.         ret
  133.  
  134. ; -----
  135. ; exec_user_cmd - set up and issue the int 21 function 4b (exec)
  136. ; -----
  137. exec_user_cmd:
  138.         mov     cs:exec_sp,sp           ; save register
  139.         mov     ax,cs:[2ch]             ; pass address of our environment
  140.         mov     exec_env,ax             ; to exec function
  141.         mov     word ptr cmdline_seg,ds ; address of command line
  142.         mov     word ptr fcb1_seg,ds    ; fill in segments for fcbs
  143.         mov     word ptr fcb2_seg,ds
  144.         push    cs
  145.         pop     es
  146.         mov     bx,offset exec_parm_block       ; bx = exec parameter block
  147.         lds     dx,commandcom_addr      ; es:bx = asciiz string of command.com
  148.         mov     ax,4b00h                ; code to load and execute a program
  149.         int     21h                     ; tell dos to execute the user's program
  150.         mov     ds,cs:swappee_psp       ; restore ds addressability
  151.         cli                             ; turn off interrupts
  152.         mov     ss,swappee_psp          ; restore stack
  153.         mov     sp,exec_sp
  154.         sti                             ; allow interrupts
  155.         ret
  156.  
  157. ; -----
  158. ; copy_stop - close handle and restore original int 21 vector
  159. ; -----
  160. copy_stop:
  161.         test    cs:flag,flag_copy       ; did we copy stdout to display?
  162.         jz      copy_stop_ret           ; no
  163. ; ----- close handle for console
  164.         mov     bx,handle               ; close handle for 'con'
  165.         mov     ah,3eh                  ; dos function = close handle
  166.         int     21h                     ; tell dos to close 'con'
  167. ; ----- restore original int 21 vector
  168.         push    ds                      ; ds gets clobbered, so save it
  169.         lds     dx,int21_vector         ; get address of old int 21 vector
  170.         mov     ax,2521h                ; code to set interrupt 21 address
  171.         int     21h                     ; tell dos to change it
  172.         pop     ds                      ; restore ds addressability
  173. copy_stop_ret:
  174.         ret
  175.  
  176. ; -----
  177. ; swap_in - swap in all but the first page of swappee
  178. ; -----
  179. swap_in:
  180.         mov     bx,cs                   ; bx = swappee's psp
  181.         add     bx,3ffh                 ; first page to swap in over
  182.         mov     es,bx
  183.         test    flag,flag_disk
  184.         jnz     swap_in_disk
  185. ; ----- swap in from expanded memory
  186.         mov     cx,1                    ; start with second logical page
  187.         cld
  188. swap_in_page:                           ; loop to swap 16K
  189.         mov     bx,cx                   ; logical page
  190.         call    map_page
  191.         push    ds                      ; save ds
  192.         mov     ds,ems_frame            ; ds = where to swap from
  193.         mov     si,0
  194.         mov     di,0
  195.         push    cx
  196.         mov     cx,4000h                ; copy 16K
  197.         rep     movsb
  198.         pop     cx
  199.         pop     ds                      ; restore ds
  200.         mov     bx,es
  201.         add     bx,400h
  202.         mov     es,bx                   ; es = next place to swap to
  203.         inc     cx
  204.         cmp     cx,swap_pages
  205.         jl      swap_in_page
  206.         ret
  207. ; ----- swap in from disk
  208. swap_in_disk:                           ; es = first page to swap over
  209.         call    open_swap_file          ; open the swap file
  210.         mov     cx,0                    ; high order part of offset
  211.         mov     dx,4000h                ; file pointer to start + 16k
  212.         mov     bx,swap_handle          ; get swap file handle
  213.         mov     ax,4201h                ; code to lseek from current location
  214.         int     21h                     ; tell dos to lseek to 2nd page
  215.         jnc     lseek_done
  216.         error   "LSEEK on swap file failed"
  217. lseek_done:
  218.         mov     cx,1                    ; start with second logical page
  219. swap_in_disk_page:                      ; loop to swap 16K
  220.         call    read_swap_file          ; read 16k from swap file
  221.         mov     bx,es
  222.         add     bx,400h
  223.         mov     es,bx                   ; es = next place to swap to
  224.         inc     cx
  225.         cmp     cx,swap_pages
  226.         jl      swap_in_disk_page
  227.         call    close_swap_file
  228.         ret
  229.  
  230. ; -------------------------------------------------------------------
  231. ; int_21_handler and its subroutines follow
  232. ; -------------------------------------------------------------------
  233.         assume  ds:nothing
  234. int21_handler:
  235. ; ----- decide whether we will front-end this int 21 function
  236.         cmp     ah,02h
  237.         je      func02
  238.         cmp     ah,06h
  239.         je      func06
  240.         cmp     ah,09h
  241.         je      func09
  242.         cmp     ah,40h
  243.         je      func40
  244. ; ----- call the original int 21 vector owner
  245. do_real_thing:
  246.         jmp     cs:int21_vector
  247.  
  248. ; -----
  249. ; handle int 21 function 9 (print dollar-sign delimited string)
  250. ; -----
  251. func09:
  252.         call    front_start
  253.         push    di
  254.         push    es
  255.         mov     di,dx
  256.         mov     es,save_ds              ; address of string at es:di
  257.         mov     al,'$'                  ; scan for $
  258.         mov     cx,-1                   ; max bytes to scan
  259.         cld                             ; scan in forward direction
  260.         repne   scasb                   ; find the $
  261.         sub     di,dx
  262.         mov     cx,di                   ; length to write
  263.         dec     cx                      ; don't write the $
  264.         pop     es
  265.         pop     di
  266.         mov     ds,save_ds              ; ds addressability is blown
  267.         call    write_to_con            ; write buffer to display
  268.         mov     ds,cs:swappee_psp       ; restore ds addressability
  269.         jmp     front_done
  270.  
  271. ; -----
  272. ; handle int 21 function 6 (direct console i/o)
  273. ; -----
  274. func06:
  275.         cmp     dl,0ffh                 ; get input characters?
  276.         je      do_real_thing           ; yes, then there is no output to copy
  277.  
  278. ; -----
  279. ; handle int 21 function 2 (display character in dl register)
  280. ; -----
  281. func02:
  282.         call    front_start
  283.         mov     char_buf,dl             ; put character to write in buffer
  284.         mov     dx,offset char_buf      ; get address of buffer
  285.         mov     cx,1                    ; get length
  286.         call    write_to_con            ; write buffer to display
  287.         jmp     front_done
  288.  
  289. ; -----
  290. ; handle int 21 function 40 (write to file handle)
  291. ; -----
  292. func40:
  293.         call    front_start
  294. ; ----- verify that file handle array entry for this handle == stdout entry
  295.         push    di
  296.         push    es
  297.         mov     bx,save_bx              ; get caller's handle
  298.         mov     es,save_pid             ; psp for process issuing int 21
  299.         les     di,es:34h               ; address of caller's file handle array
  300.         mov     ah,es:[di+1]            ; file handle array entry for stdout
  301.         cmp     ah,es:[di+bx]           ; does handle entry == stdout entry?
  302.         pop     es
  303.         pop     di
  304.         jne     func40_done             ; no, don't copy to console
  305. ; ----- call real int 21 handler with handle opened for 'con'
  306.         mov     ds,save_ds              ; ds addressability blown
  307.         call    write_to_con            ; write buffer to display
  308.         mov     ds,cs:swappee_psp       ; restore ds addressability
  309. func40_done:
  310.         jmp     front_done
  311.  
  312. ; -----
  313. ; front_start - start front-ending int 21
  314. ; -----
  315. front_start:
  316.         assume  ds:nothing
  317. ; ----- establish ds addressability and save registers
  318.         mov     save_ds,ds
  319.         mov     ds,cs:swappee_psp       ; establish ds addressability
  320.         assume  ds:code                 ; tell assembler
  321.         mov     save_ax,ax              ; save registers
  322.         mov     save_bx,bx
  323.         mov     save_cx,cx
  324.         mov     save_dx,dx
  325. ; ----- remember caller's pid
  326.         mov     ah,51h                  ; dos function = get pid
  327.         int     21h                     ; tell dos to get pid
  328.         mov     save_pid,bx             ; remember pid
  329. ; ----- set pid so our file handle array is used
  330.         mov     bx,cs                   ; pid = my cs register
  331.         mov     ah,50h                  ; dos function = set pid
  332.         int     21h                     ; tell dos to set pid
  333.         ret
  334.  
  335. ; -----
  336. ; write_to_con - call original int 21H handler to write buffer to display
  337. ; -----
  338. write_to_con:
  339.         assume  ds:nothing
  340.         mov     bx,cs:handle            ; handle opened for 'con'
  341.         mov     ah,40h                  ; dos function = write to handle
  342.         pushf
  343.         call    dword ptr cs:int21_vector       ; call dos
  344.         ret
  345.  
  346. ; -----
  347. ; front_done - almost done front-ending int 21
  348. ; -----
  349. front_done:
  350.         assume  ds:code
  351. ; ----- restore caller's pid
  352.         mov     bx,save_pid             ; get pid of process that issued int 21
  353.         mov     ah,50h                  ; dos function = set pid
  354.         int     21h                     ; set pid
  355. ; ----- restore registers & go jump to previous int 21 handler
  356.         mov     ax,save_ax
  357.         mov     bx,save_bx
  358.         mov     cx,save_cx
  359.         mov     dx,save_dx
  360.         mov     ds,save_ds              ; ds addressability blown
  361.         jmp     do_real_thing
  362.  
  363. ; -------------------------------------------------------------------
  364. ; the following routines are used by both parts of the program
  365. ; -------------------------------------------------------------------
  366.  
  367. ; -----
  368. ; emm - remember emm function in case of error and issue int 67
  369. ; -----
  370. emm:
  371.         mov     last_ems_func,ah
  372.         int     67h                     ; call expanded memory manager
  373.         or      ah,ah
  374.         ret
  375.  
  376. ; -----
  377. ; ems_error - handle ems errors
  378. ; -----
  379. ems_error:
  380.         mov     di,offset ems_rc
  381.         call    hex_to_ascii            ; make ems error code printable
  382.         mov     ah,last_ems_func
  383.         mov     di,offset ems_func
  384.         call    hex_to_ascii            ; make last ems function printable
  385.         mov     cx,emsg_ems_len
  386.         mov     dx,offset emsg_ems
  387.         jmp     error_exit              ; go display error message and exit
  388.  
  389. ; ------
  390. ; hex_to_ascii - convert ah register contents to ascii hexadecimal at ds:di
  391. ; ------
  392. hex_to_ascii:
  393.         mov     dl,ah
  394.         mov     cx,2
  395. hex_char:
  396.         push    cx
  397.         mov     cl,4
  398.         rol     dl,cl
  399.         mov     al,dl
  400.         and     al,00fh
  401.         daa
  402.         add     al,0f0h
  403.         adc     al,040h
  404.         mov     [di],al
  405.         inc     di
  406.         pop     cx
  407.         loop    hex_char
  408.         ret
  409.  
  410. ; -----
  411. ; error_exit - display error message and exit
  412. ; ds:dx point to error message, cx has the length
  413. ; -----
  414. error_exit:
  415.         push    cx
  416.         push    dx
  417.         mov     dx,offset emsg_start
  418.         mov     cx,emsg_start_len
  419.         mov     bx,2                    ; handle for stderr
  420.         mov     ah,40h                  ; dos function = handle write
  421.         int     21h                     ; output error message to stderr
  422.         pop     dx
  423.         pop     cx
  424.         mov     bx,2                    ; handle for stderr
  425.         mov     ah,40h                  ; dos function = handle write
  426.         int     21h                     ; output error message to stderr
  427.         jmp     return
  428.  
  429. ; -----
  430. ; routines to open, read from, and close the swap file
  431. ; -----
  432. open_swap_file:
  433.         mov     dx,offset swap_fid      ; address of fileid to open
  434.         mov     ax,3d00h                ; open file in read-only mode
  435.         int     21h
  436.         jnc     open_exit
  437.         error   "Could not open swap file"
  438. open_exit:
  439.         mov     swap_handle,ax
  440.         ret
  441.  
  442. ; read_swap_file - read 16K from swap file to address in es:0
  443. ; saves cx
  444. read_swap_file:
  445.         push    cx
  446.         mov     bx,swap_handle          ; get swap file handle
  447.         mov     cx,4000h                ; read 16k
  448.         mov     dx,0                    ; buffer offset
  449.         push    ds
  450.         push    es
  451.         pop     ds                      ; buffer segment
  452.         mov     ah,3fh                  ; dos function = handle read
  453.         int     21h
  454.         pop     ds
  455.         pop     cx
  456.         jnc     read_exit
  457.         error   "Error reading swap file"
  458. read_exit:
  459.         ret
  460.  
  461. close_swap_file:
  462.         mov     bx,swap_handle          ; get swap file handle
  463.         mov     ah,3eh                  ; dos function = close file
  464.         int     21h
  465.         ret
  466.  
  467. ; -----
  468. ; return - return to DOS
  469. ; -----
  470. return:
  471.         mov     ax,4c00h                ; dos function to terminate
  472.         int     21h                     ; back to dos
  473.  
  474. ; -----
  475. ; map_page - map EMS logical page in bx into physical page 0
  476. ; -----
  477. map_page:
  478.         mov     al,0                    ; physical page
  479.         mov     dx,ems_handle           ; ems handle
  480.         mov     ah,44h                  ; map handle page
  481.         call    emm
  482.         jz      map_page_exit
  483.         jmp     ems_error
  484. map_page_exit:
  485.         ret
  486.  
  487. lowend  equ     $                       ; end of code copied to lower memory
  488.  
  489. ; -------------------------------------------------------------------
  490. ; the following is *not* copied on top of the swappee
  491. ; -------------------------------------------------------------------
  492.  
  493. hello           db      "SWAP Version 1.0 (c) Copyright 1988 Nico Mak"
  494.                 db      " and Mansfield Software Group", cr, lf
  495. hello_len       equ     $-hello
  496. emsg_start      db      "SWAP Error: "
  497. emsg_start_len  equ     $-emsg_start
  498. run_addr        dw      offset run_command     ; offset of run_command
  499. run_seg         dw      ?                      ; segment of run_command
  500. swappee_mcb     dw      ?               ; segment of mcb for swappee psp
  501. swappee_end     dw      ?               ; segment of mcb after swappee
  502. my_mcb_size     dw      ?
  503. next_mcb        dw      ?               ; address of next mcb
  504. next_code       db      ?               ; M/Z code in next MCB
  505. next_owner      dw      ?               ; etc
  506. next_size       dw      ?               ;
  507. ems_device_name db      "EMMXXXX0",0    ; expanded memory manager signature
  508. comspec         db      'COMSPEC='      ; environment variable name
  509. comspec_len     equ     $-comspec
  510.  
  511. mcb_info        struc                   ; important memory control block info
  512. addr            dw      ?               ; address of mcb
  513. owner           dw      ?               ; psp of owner
  514. len             dw      ?               ; length of mcb
  515. mcb_info        ends
  516.  
  517. max_mcbs        equ     100
  518. mcbs            mcb_info <>
  519. mcb_length      equ     $-mcbs
  520.                 db      (max_mcbs-1)*mcb_length dup (?)
  521.  
  522. ; -------------------------------------------------------------------
  523. ; mainline code run from system prompt
  524. ; -------------------------------------------------------------------
  525. begin:
  526.         assume  ds:code,es:code
  527.         mov     sp,offset stack         ; set up new stack pointer
  528.         call    process_cmdline         ; check options, set up 'exec' cmdline
  529.         call    say_hello               ; print copyright message
  530.         call    check_dos_version       ; ensure we have dos 3.0 or later
  531.         call    find_comspec            ; find comspec= in environment
  532.         call    shrink_ourself          ; free unneeded memory
  533.         call    get_mcb_info            ; get relevant info about mcbs
  534.         call    check_mcbs              ; ensure mcbs are in expected order
  535.         call    vector_check            ; ensure swappee has not hooked vectors
  536.         call    figure_pages            ; determine how many ems pages we need
  537.         call    init_ems                ; ems initialization, allocation, etc
  538.         call    swap_out                ; swap out swappee, command.com, and us
  539.         call    muck_with_memory        ; copy swap over swappee & set up mcbs
  540.         mov     ss,swappee_psp          ; switch to stack in low memory
  541.         call    run_user_command        ; go call run_command rtn in low memory
  542.         mov     ss,my_psp               ; switch back to original stack
  543.         call    swap_first              ; swap in first 16K
  544.         call    clean_up                ; restore original environment
  545. exit:
  546.         jmp     return                  ; leave SWAP
  547.  
  548. ; -------------------------------------------------------------------
  549. ; subroutines for code that is not copied to low memory follow
  550. ; -------------------------------------------------------------------
  551.  
  552. ; -----
  553. ; process_cmdline - process options, set up command line for exec function
  554. ; -----
  555. process_cmdline:
  556.         mov     bx,80h
  557. option_check:
  558.         inc     bx
  559.         cmp     byte ptr [bx],cr        ; carriage return?
  560.         jne     option_check2           ; no
  561.         error   "No command to execute"
  562. option_check2:
  563.         cmp     byte ptr [bx],' '       ; blank?
  564.         je      option_check
  565.         cmp     byte ptr [bx],'/'       ; option signal?
  566.         je      got_option
  567.         cmp     byte ptr [bx],'-'       ; option signal?
  568.         jne     copy_command_line
  569. got_option:
  570.         mov     byte ptr [bx],' '       ; blank out character on command line
  571.         inc     bx                      ; point at option
  572.         mov     al,byte ptr [bx]        ; get option
  573.         mov     byte ptr [bx],' '       ; blank out character on command line
  574.         or      al,' '                  ; convert option to lower case
  575.         cmp     al,'c'                  ; option 'c'?
  576.         jne     check_option_q
  577.         or      flag,flag_copy
  578.         jmp     option_check
  579. check_option_q:
  580.         cmp     al,'q'                  ; option 'q'?
  581.         jne     check_option_f
  582.         or      flag,flag_quiet
  583.         jmp     option_check
  584. check_option_f:
  585.         cmp     al,'f'                  ; option 'f'?
  586.         jne     check_option_d
  587.         or      flag,flag_force
  588.         jmp     option_check
  589. check_option_d:
  590.         cmp     al,'d'                  ; option 'd'?
  591.         jne     bad_option
  592.         or      flag,flag_disk
  593.         jmp     option_check
  594. bad_option:
  595.         error   "Invalid option"
  596. ; ----- copy remainder of our command line to command line for command.com
  597. copy_command_line:
  598.         mov     cl,ds:[80h]             ; length of my command line
  599.         inc     cl                      ; add one for cr
  600.         mov     si,81h                  ; address of my command line
  601.         mov     di,offset command_text  ; address of where to put it
  602.         xor     ch,ch                   ; zero uninitialized part of count
  603.         cld                             ; scan in forward direction
  604.         rep     movsb                   ; copy command line
  605. ; set length of new command line
  606.         mov     cl,ds:[80h]             ; length of my command line
  607.         add     cl,2                    ; add 2 for "/c"
  608.         mov     command_line,cl         ; save new length
  609.         ret
  610.  
  611. ; -----
  612. ; say_hello - print hello message
  613. ; -----
  614. say_hello:
  615.         test    flag,flag_quiet         ; was -q option used?
  616.         jnz     say_hello_exit          ; yes, skip this
  617.         mov     dx,offset hello         ; get address of message
  618.         mov     cx,hello_len            ; get length of message
  619.         mov     bx,2                    ; handle for stderr
  620.         mov     ah,40h                  ; dos function = write to handle
  621.         int     21h                     ; write copyright message
  622. say_hello_exit:
  623.         ret
  624.  
  625. ; -----
  626. ; check_dos_version - be sure this is dos 3.0 or higher
  627. ; -----
  628. check_dos_version:
  629.         mov     ah,30h                  ; dos function = get version
  630.         int     21h                     ; get dos version
  631.         cmp     al,3                    ; ok?
  632.         jae     dos_version_ret
  633.         error   "DOS version must be 3.0 or higher"
  634. dos_version_ret:
  635.         ret
  636.  
  637. ; -----
  638. ; find_comspec - find fileid for exec function
  639. ; -----
  640. find_comspec:
  641.         mov     es,es:2ch               ; es = environment segment
  642.         xor     di,di                   ; point to start of env in es:di
  643.         cld                             ; scan in forward direction
  644. ; ----- loop thru environment strings one by one, beginning here
  645. find_string:
  646.         test    byte ptr es:[di],-1     ; end of environment?
  647.         jnz     check_string            ; nope, continue
  648.         error   "Could not find COMSPEC= in environment" ; very unlikely
  649. ; ----- compare current env string to 'COMSPEC='
  650. check_string:
  651.         mov     si,offset comspec       ; point to 'COMSPEC=' string
  652.         mov     bx,di                   ; save ptr to start of env string
  653.         mov     cx,comspec_len          ; length of 'COMSPEC='
  654.         repe    cmpsb                   ; compare
  655.         je      found_comspec           ; found it
  656.         mov     di,bx                   ; restore ptr to start of env string
  657.         xor     al,al                   ; scan for end of string
  658.         mov     cx,-1
  659.         repne   scasb
  660.         jmp     find_string             ; go back for next string
  661. ; ----- found COMSPEC=
  662. found_comspec:
  663.         mov     word ptr commandcom_addr[0],di  ; remember address of ...
  664.         mov     word ptr commandcom_addr[2],es  ; ... asciiz "command.com"
  665.         ret
  666.  
  667. ; -----
  668. ; shrink_ourself - release unneeded memory
  669. ; -----
  670. shrink_ourself:
  671.         push    cs
  672.         pop     es                      ; address of start of SWAP memory
  673.         mov     bx,offset endcode+15    ; address of end of SWAP code
  674.         mov     cl,4
  675.         shr     bx,cl                   ; convert to paragraphs
  676.         mov     ah,4ah                  ; dos function = SETBLOCK
  677.         int     21h                     ; shrink ourselves
  678.         ret
  679.  
  680. ; -----
  681. ; get_mcb_info - get relevant info from mcb chain
  682. ; -----
  683. get_mcb_info:
  684.         mov     my_psp,cs               ; remember address of our PSP
  685.         mov     ah,52h                  ; undocumented function
  686.         int     21h                     ; get base of memory chain
  687.         mov     es,es:[bx]-2            ; this is it
  688.         mov     bx,offset mcbs
  689.         mov     dx,0                    ; count of MCBs
  690. mem_loop:
  691.         mov     [bx].addr,es
  692.         mov     cx,word ptr es:1        ; owner of mcb
  693.         mov     [bx].owner,cx
  694.         mov     cx,word ptr es:3        ; length of mcb
  695.         mov     [bx].len,cx
  696.         inc     dx                      ; increment count of MCBs
  697.         cmp     dx,max_mcbs
  698.         jle     mem_loop1
  699.         error   "Over 100 Memory Control Blocks in system"
  700. mem_loop1:
  701.         cmp     byte ptr es:0,'Z'       ; last memory block?
  702.         jne     mem_next
  703.         error   "Could not find SWAP's PSP"
  704. mem_next:
  705.         mov     cx,es                   ; copy seg addr of mcb
  706.         inc     cx                      ; next paragraph
  707.         cmp     cx,my_psp               ; is this our psp?
  708.         je      found_our_psp           ; yes
  709.         add     cx,[bx].len             ; add length of this mcb
  710.         mov     es,cx                   ; this is next memory block
  711.         add     bx,mcb_length           ; where next mcb goes
  712.         jmp     mem_loop                ; proceed
  713. found_our_psp:                          ; have found our psp
  714.         mov     dx,[bx].len
  715.         mov     my_mcb_size,dx          ; remember length of our mcb
  716.         add     cx,[bx].len             ; add length of memory
  717.         mov     next_mcb,cx             ; this is next memory block
  718. ; ----- remember information about the next mcb
  719.         mov     es,cx
  720.         mov     dl,es:0
  721.         mov     next_code,dl
  722.         mov     dx,es:1
  723.         mov     next_owner,dx
  724.         mov     dx,es:3
  725.         mov     next_size,dx
  726.         ret
  727.  
  728. ; -----
  729. ; check_mcbs - ensure mcbs are in expected order
  730. ; verify that our parent is command.com, find swappee psp, etc.
  731. ; -----
  732. check_mcbs:
  733.         mov     cx,cs:16h               ; our parent's address
  734.         mov     es,cx
  735.         mov     ax,es:16h               ; and our grandparent's address
  736.         cmp     ax,cx                   ; better be equal
  737.         jne     unknown_parent
  738.         mov     ax,cs:10h               ; our ctrl-break handler
  739.         cmp     ax,cx                   ; better equal our parent's address
  740.         je      skip_our_env
  741. unknown_parent:
  742.         error   "SWAP not directly run from COMMAND.COM"
  743. ; ----- back up to find swappee's mcb.  bx still points at entry for our mcb
  744. skip_our_env:
  745.         mov     cx,cs
  746.         call    prev_mcb
  747.         cmp     [bx].owner,cx           ; is this mcb for our environment?
  748.         jne     skip_command
  749.         call    prev_mcb
  750. ; ----- back up over all mcb's owned by command.com (es == command.com psp)
  751. skip_command:
  752.         mov     cx,es                   ; address of command.com psp
  753.         cmp     [bx].owner,cx           ; is this mcb owned by command.com?
  754.         je      command_loop            ; yes
  755.         error   "COMMAND.COM must immediately precede SWAP in memory"
  756. command_loop:
  757.         mov     dx,[bx].addr            ; remember address of mcb in case
  758.         mov     swappee_end,dx          ;   it is the one above swappee
  759.         call    prev_mcb                ; back up one mcb
  760.         cmp     [bx].owner,cx           ; is this mcb owned by command.com?
  761.         je      command_loop            ; yes, skip it
  762. ; ----- assume we have one of swappee's mcbs
  763. ;       back up over all it's mcb's till we reach psp
  764.         mov     cx,[bx].owner           ; cx = swappee's psp
  765. find_swappee_psp:
  766.         mov     dx,[bx].addr            ; address of this mcb
  767.         inc     dx                      ; address of memory
  768.         cmp     dx,cx                   ; is this swappee's psp?
  769.         je      found_swappee_psp       ; yes
  770.         call    prev_mcb                ; check previous psp
  771.         cmp     [bx].owner,cx           ; still owned by swappee?
  772.         je      find_swappee_psp        ; yes continue
  773.         error   "Unexpected MCB while looking for PSP of swappee"
  774. ; ----- we've found swappee's psp - bx points at mcb entry for swappee
  775. found_swappee_psp:
  776.         mov     es,[bx].owner           ; es = swappee's psp
  777.         mov     swappee_psp,es          ; remember swappee's psp
  778.         cmp     word ptr es:2ch,0       ; swappee must have an environment
  779.         jne     check_mcbs_ret
  780.         error   "Swappee does not have an environment"
  781. check_mcbs_ret:
  782.         ret
  783.  
  784. ; -----
  785. ; unless the -f option was specified, check whether vectors point at swappee
  786. ; note: only interrupts 1-79h (inclusive) are checked
  787. ; -----
  788. vector_check:
  789.         test    flag,flag_force
  790.         jnz     vector_check_ret
  791.         mov     cx,0                    ; start at the beginning
  792. next_vector:
  793.         inc     cx                      ; next vector
  794.         cmp     cx,80h                  ; all done?
  795.         jae     vector_check_ret        ; yes, no vectors hooked
  796.         mov     ah,35h                  ; get vector function
  797.         mov     al,cl                   ; vector number
  798.         int     21h                     ; call dos to get vector address
  799.         mov     dx,es                   ; get segment addr
  800.         push    cx
  801.         mov     cl,4                    ; shift count
  802.         add     bx,15                   ; round up
  803.         shr     bx,cl                   ; divide offset by 16
  804.         pop     cx
  805.         add     dx,bx                   ; compute segment
  806.         cmp     swappee_psp,dx          ; compare to start of swappee
  807.         jae     next_vector             ; no problem, keep looking
  808.         cmp     dx,swappee_end          ; compare to end of swappee
  809.         jae     next_vector             ; no problem either
  810.         error   "Swappee has hooked an interrupt vector"
  811. vector_check_ret:
  812.         ret
  813.  
  814. ; -----
  815. ; figure_pages - figure how many 16K pages of EMS we need
  816. ; -----
  817. figure_pages:
  818.         mov     cx,swappee_psp
  819.         dec     cx                      ; cx = swappee's mcb
  820.         mov     swappee_mcb,cx          ; remember address of mcb
  821.         mov     dx,next_mcb             ; dx = mcb after swap.com
  822.         sub     dx,cx                   ; dx = difference in paragraphs
  823.         mov     cx,10
  824.         shr     dx,cl                   ; convert paragraphs to 16k pages
  825.         or      dx,dx
  826.         jnz     figure2
  827.         error   "Less than 16K to swap"
  828. figure2:
  829.         inc     dx
  830.         mov     swap_pages,dx
  831.         ret
  832.  
  833. ; -----
  834. ; init_ems - ensure ems is up to par, allocate pages, and save page map
  835. ; -----
  836. init_ems:
  837.         test    flag,flag_disk
  838.         jz      find_emm
  839.         jmp     init_ems_exit
  840. ; ----- determine whether ems is installed
  841. find_emm:
  842.         mov     ax,3567h                ; code to get int 67 handler address
  843.         int     21h                     ; get interrupt vector
  844.         mov     di,0ah                  ; offset to name string
  845.         mov     si,offset ems_device_name ; correct ems name
  846.         mov     cx,8                    ; length of name
  847.         cld                             ; scan in forward direction
  848.         repe    cmpsb                   ; do the compare
  849.         jz      test_status             ; ems not loaded
  850.         error   "Could not find Expanded Memory Manager"
  851. ; ----- test ems status
  852. test_status:
  853.         mov     ah,40h                  ; code to test status
  854.         call    emm
  855.         jz      check_ems_version
  856.         jmp     ems_error
  857. ; ----- ensure that we have ems version 3.2 or later
  858. check_ems_version:
  859.         mov     ah,46h                  ; get version
  860.         call    emm
  861.         jz      got_ems_version
  862.         jmp     ems_error
  863. got_ems_version:
  864.         cmp     al,32h
  865.         jnb     get_page_frame
  866.         error   "Expanded Memory Manager version must be 3.2 or higher"
  867. ; ----- get page frame address
  868. get_page_frame:
  869.         mov     ah,41h                  ; code to get page frame addr
  870.         call    emm
  871.         mov     ems_frame,bx            ; where ems memory starts
  872.         jz      alloc_pages
  873.         jmp     ems_error
  874. ; ----- allocate ems pages
  875. alloc_pages:
  876.         mov     ah,43h
  877.         mov     bx,swap_pages
  878.         call    emm
  879.         mov     ems_handle,dx
  880.         jz      save_page_map
  881.         error   "Not enough free expanded memory"
  882. ; ----- save ems page map
  883. save_page_map:
  884.         mov     ah,47h                  ; save page map
  885.         mov     dx,ems_handle
  886.         call    emm
  887.         jz      init_ems_exit
  888.         jmp     ems_error
  889. init_ems_exit:
  890.         ret
  891.  
  892. ; -----
  893. ; swap_out - swap out swappee, command.com, and ourself
  894. ; -----
  895. swap_out:
  896.         mov     es,swappee_mcb
  897.         test    flag,flag_disk          ; swap to disk?
  898.         jnz     swap_out_disk           ; yes
  899. ; ----- swap out to expanded memory
  900.         mov     cx,0
  901.         cld
  902. swap_out_page:                          ; loop to swap 16K
  903.         mov     bx,cx                   ; logical page = loop count
  904.         call    map_page
  905.         mov     bx,ems_frame
  906.         assume  ds:nothing
  907.         push    es
  908.         pop     ds                      ; ds = where to swap from
  909.         mov     es,bx                   ; es = ems_frame
  910.         mov     si,0
  911.         mov     di,0
  912.         push    cx
  913.         mov     cx,4000h                ; copy 16K
  914.         rep     movsb
  915.         pop     cx
  916.         mov     bx,ds                   ; where to swap from
  917.         add     bx,400h                 ; add 16K
  918.         mov     es,bx                   ; es = next place to swap from
  919.         push    cs
  920.         pop     ds
  921.         assume  ds:code
  922.         inc     cx
  923.         cmp     cx,swap_pages           ; done swapping?
  924.         jl      swap_out_page           ; no, swap the next page
  925.         ret
  926. ; ----- swap out to disk
  927. swap_out_disk:                          ; es = swappee's mcb
  928.         mov     cx,0                    ; attribute
  929.         mov     dx,offset swap_fid
  930.         mov     ah,3ch                  ; dos function = create a file
  931.         int     21h
  932.         jnc     create_done
  933.         error   "Could not create swap file"
  934. create_done:
  935.         mov     swap_handle,ax
  936.         mov     cx,0                    ; number of pages swapped
  937. swap_out_disk_page:                     ; loop to swap 16K
  938.         push    cx                      ; remember number pages swapped
  939.         mov     bx,swap_handle          ; handle to write to
  940.         mov     cx,04000h               ; write 16k
  941.         xor     dx,dx                   ; offset to write from
  942.         push    ds
  943.         push    es
  944.         pop     ds                      ; segment to write from
  945.         mov     ah,40h                  ; dos function = write to handle
  946.         int     21h
  947.         pop     ds
  948.         jnc     write_worked1
  949.         error   "Error writing to swap file"
  950. write_worked1:
  951.         mov     bx,es                   ; where to swap from
  952.         add     bx,400h                 ; add 16K
  953.         mov     es,bx                   ; es = next place to swap from
  954.         pop     cx                      ; remember number of pages swapped
  955.         inc     cx                      ; now we've swapped one more page
  956.         cmp     cx,swap_pages           ; done swapping?
  957.         jl      swap_out_disk_page      ; no, swap the next page
  958.         call    close_swap_file
  959.         ret
  960.  
  961. ; -----
  962. ; muck_with_memory - copy part of SWAP over swappee's psp, set up mcbs, etc
  963. ; -----
  964. muck_with_memory:
  965.         mov     es,swappee_psp
  966. ; ----- copy code over swappee's psp
  967.         cld                             ; copy in forward direction
  968.         mov     cx,offset lowend        ; length of code to copy
  969.         mov     si,100h                 ; start copying after psp
  970.         mov     di,100h                 ; where to copy
  971.         rep     movsb                   ; copy code over swappee's psp
  972. ; ----- copy our file handle array down to swappee's psp
  973.         mov     cx,20                   ; length of file handle table
  974.         mov     si,18h                  ; address of our file handle table
  975.         mov     di,18h                  ; where to put file handle table
  976.         rep     movsb                   ; copy file handle table to swappee psp
  977. ; ----- set the file handle array size and offset in swappee's psp
  978.         mov     word ptr es:32h,20      ; length of file handle table
  979.         mov     word ptr es:34h,18h     ; offset of file handle table
  980.         mov     word ptr es:36h,es      ; segment of file handle table
  981. ; ----- now fix up the swappee's mcb (still has an M)
  982.         mov     es,swappee_mcb          ; address of swappee's mcb
  983.         mov     dx,offset lowend+15     ; offset to end of SWAP code
  984.         mov     cx,4
  985.         shr     dx,cl                   ; convert to paragraphs
  986.         mov     word ptr es:3,dx        ; put result in swappee's mcb
  987. ; ----- find address of mcb for memory that was freed up
  988.         mov     bx,swappee_psp          ; address of swappee's psp
  989.         add     bx,dx                   ; add paragraphs in swappee's mcb
  990.         mov     es,bx                   ; this is where mcb for free mem goes
  991. ; ----- fill in new mcb
  992.         mov     dx,next_mcb             ; address of mcb after original swap
  993.         sub     dx,bx                   ; compute paragraphs of free space
  994.         add     dx,next_size            ; add paragraphs for next mcb
  995.         mov     word ptr es:3,dx        ; fill in size
  996.         mov     dl,next_code            ; get id from next mcb
  997.         mov     byte ptr es:0,dl        ; copy id (M or Z)
  998.         mov     word ptr es:1,0         ; mark block as free
  999.         ret
  1000.  
  1001. ; -----
  1002. ; run_user_command - call run_command routine in low memory
  1003. ; -----
  1004. run_user_command:
  1005. ; ----- put swappee segment address into pointer to run_command
  1006.         mov     bx,swappee_psp
  1007.         mov     word ptr run_seg,bx     ; segment of swappee psp
  1008. ; ----- set pid to address of swappee psp
  1009.         mov     ah,50h                  ; dos function = set pid
  1010.         int     21h                     ; set process id
  1011. ; ----- call run_command in low memory
  1012.         mov     ds,bx
  1013.         assume  ds:nothing
  1014.         call    dword ptr cs:run_addr   ; call run_command
  1015.         mov     ds,cs:my_psp
  1016.         assume  ds:code
  1017. ; ----- restore pid to SWAP's psp
  1018.         mov     bx,cs                   ; pid = my cs register
  1019.         mov     ah,50h                  ; code to set pid
  1020.         int     21h
  1021.         ret
  1022.  
  1023. ; -----
  1024. ; swap_first - swap in first page that was swapped out
  1025. ; -----
  1026. swap_first:
  1027.         mov     es,swappee_mcb
  1028.         test    flag,flag_disk          ; swapping in from disk?
  1029.         jnz     swap_first_disk         ; yes
  1030. ; ----- swap in from expanded memory
  1031.         mov     bx,0                    ; logical page = 0
  1032.         call    map_page
  1033.         push    ds                      ; save ds
  1034.         mov     ds,ems_frame            ; ds = where to swap from
  1035.         mov     si,0
  1036.         mov     di,0
  1037.         mov     cx,4000h                ; copy 16K
  1038.         cld
  1039.         rep     movsb
  1040.         pop     ds                      ; restore ds
  1041.         ret
  1042. ; ----- swap in from disk
  1043. swap_first_disk:
  1044.         call    open_swap_file
  1045.         call    read_swap_file
  1046.         call    close_swap_file
  1047.         ret
  1048.  
  1049. ; -----
  1050. ; clean_up - restore ems or delete swap file
  1051. ; -----
  1052. clean_up:
  1053.         test    flag,flag_disk
  1054.         jnz     clean_up_disk
  1055. ; ----- restore ems page map
  1056.         mov     ah,48h                  ; restore page map
  1057.         mov     dx,ems_handle
  1058.         call    emm
  1059.         jz      deallocate
  1060.         jmp     ems_error
  1061. ; ----- deallocate the ems pages
  1062. deallocate:
  1063.         mov     ah,45h                  ; deallocate pages
  1064.         mov     dx,ems_handle
  1065.         call    emm
  1066.         jz      clean_up_exit
  1067.         jmp     ems_error
  1068. ; ----- delete swap disk file
  1069. clean_up_disk:
  1070.         mov     dx,offset swap_fid      ; file handle for swap file
  1071.         mov     ah,41h                  ; code to delete a file
  1072.         int     21h
  1073. clean_up_exit:
  1074.         ret
  1075.  
  1076. ; -----
  1077. ; prev_mcb - back up one entry in table of MCBs
  1078. ; -----
  1079. prev_mcb:
  1080.         sub     bx,mcb_length
  1081.         cmp     bx,offset mcbs
  1082.         jae     prev_mcb_ret
  1083.         error   "Memory Control Blocks not in expected order"
  1084. prev_mcb_ret:
  1085.         ret
  1086.  
  1087. endcode equ   $
  1088.         align   16
  1089.         db      16 dup(0)               ; so that at least on mcb follows swap
  1090. swap    endp
  1091. code    ends
  1092.         end   swap
  1093.